#
# camera.py
#
# Copyright (c) 2005 - 2007 Nokia Corporation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import e32

import _camera2 as _camera
import graphics

_my_camera=_camera.Camera(0)
number_of_devices = _my_camera.cameras_available()
device=[]
for dev_index in range(number_of_devices):
    device.append(_camera.Camera(dev_index)) #used only for image size checking
_my_call_back=None
_backlight_on=0

EOpenComplete=_camera.EOpenComplete
EPrepareComplete=_camera.EPrepareComplete
ERecordComplete=_camera.ERecordComplete

formatmap = {
    'Mono'     : _camera.EFormatMono,
    'RGB444'   : _camera.EFormat16RGB444,
    'RGB565'   : _camera.EFormat16RGB565,
    'RGB888'   : _camera.EFormat32RGB888,
    'Jpeg_Jfif': _camera.EFormatJpeg,
    'Jpeg_Exif': _camera.EFormatExif,
    'RGB12'    : _camera.EColor4K,
    'RGB16'    : _camera.EColor64K,
    'RGB24'    : _camera.EColor16M,
    'User'     : _camera.EFormatUser,
    'YUV420i'  : _camera.EFormatYUV420i,
    'YUV420p'  : _camera.EFormatYUV420p,
    'YUV422'   : _camera.EFormatYUV422,
    'YUV422r'  : _camera.EFormatYUV422r,
    'YUV444'   : _camera.EFormatYUV444,
    'YUV420sp' : _camera.EFormatYUV420sp,
    'RGB24u'   : _camera.EColor16MU 
    }
""",
'MJpeg'    : _camera.EFormatMJPEG,
'H264'     : _camera.EFormatH264}  # New!
'YUV422r2' : _camera.EFormatYUV422r2
"""

modemap = {'RGB': _camera.EColor16M,
         'RGB16': _camera.EColor64K,
         'RGB12': _camera.EColor4K,
         'JPEG_Exif': _camera.EFormatExif,
         'JPEG_JFIF': _camera.EFormatJpeg}

"""
Enum TFlash
TFlash - Specifies the type of flash.
    EFlashNone - No flash, always supported.
    EFlashAuto - Flash will automatically fire when required.
    EFlashForced - Flash will always fire.
    EFlashFillIn - Reduced flash for general lighting
    EFlashRedEyeReduce - Red-eye reduction mode.
    EFlashSlowFrontSync - Flash at the moment when shutter opens.
    EFlashSlowRearSync - Flash at the moment when shutter closes.
    EFlashManual - User configurable setting
    EFlashVideoLight - Constant emission of light during video mode - Note: This value is available only to the API clients using CCamera::New2L() or CCamera::NewDuplicate2L() 
"""
flashmap = {'none': _camera.EFlashNone,
          'auto': _camera.EFlashAuto,
          'forced': _camera.EFlashForced,
          'fill_in': _camera.EFlashFillIn,
          'red_eye_reduce': _camera.EFlashRedEyeReduce}

"""
From http://library.forum.nokia.com/index.jsp?topic=/S60_3rd_Edition_Cpp_Developers_Library/GUID-35228542-8C95-4849-A73F-2B4F082F0C44/html/SDL_93/doc_source/reference/reference-cpp/ECAM/CCameraClass.html
  Enum TWhiteBalance - Specifies how the white balance is set.
    EWBAuto - Set white balance automatically. Default, always supported.
    EWBDaylight - Normal daylight.
    EWBCloudy - Overcast daylight.
    EWBTungsten - Tungsten filament lighting.
    EWBFluorescent - Fluorescent tube lighting
    EWBFlash - Flash lighting.
    EWBSnow - High contrast daylight primarily snowy
    EWBBeach - High contrast daylight primarily near the sea
    EWBManual - User configurable mode
    EWBShade - Shade 
"""
whitebalancemap = {'auto': _camera.EWBAuto,
                 'daylight': _camera.EWBDaylight,
                 'cloudy': _camera.EWBCloudy,
                 'tungsten': _camera.EWBTungsten,
                 'fluorescent': _camera.EWBFluorescent,
                 'flash': _camera.EWBFlash}

"""
From http://library.forum.nokia.com/index.jsp?topic=/S60_3rd_Edition_Cpp_Developers_Library/GUID-35228542-8C95-4849-A73F-2B4F082F0C44/html/SDL_93/doc_source/reference/reference-cpp/ECAM/CCameraClass.html
  Enum TExposure - Specifies the type of exposure.
    EExposureAuto is the default value.
    EExposureAuto - Set exposure automatically. Default, always supported.
    EExposureNight - Night-time setting for long exposures.
    EExposureBacklight - Backlight setting for bright backgrounds.
    EExposureCenter - Centered mode for ignoring surroundings.
    EExposureSport - Sport setting for very short exposures.
    EExposureVeryLong - Generalised setting for very long exposures.
    EExposureSnow - Snow setting for daylight exposure.
    EExposureBeach - Beach setting for daylight exposure with reflective glare.
    EExposureProgram - Programmed exposure setting.
    EExposureAperturePriority - Aperture setting is given priority.
    EExposureShutterPriority - Shutter speed setting is given priority.
    EExposureManual - User selectable exposure value setting.
    EExposureSuperNight - Exposure night setting with colour removed to get rid of colour noise.
    EExposureInfra - Exposure for infra-red sensor on the camera
"""
exposuremap = {'auto': _camera.EExposureAuto,
             'night': _camera.EExposureNight,
             'backlight': _camera.EExposureBacklight,
             'center': _camera.EExposureCenter}

def _finder_call_back(image_frame):
    global _backlight_on
    global _my_call_back
    if(_backlight_on):
        e32.reset_inactivity()
    _my_call_back(graphics.Image.from_cfbsbitmap(image_frame))

def _main_pane_size():
    try:
        if e32.s60_version_info >= (2, 8):
            import appuifw
            return appuifw.app.layout(appuifw.EMainPane)[0]
    except:
        pass
    return (176, 144) # hard coded default

def take_photo(mode = "RGB16", size = (640, 480), zoom = 0, flash = "none",
               exposure = "auto", white_balance = "auto", position = 0):
    s = -1
    if (position >= number_of_devices):
        raise ValueError, "Camera position not supported"  
    for i in range(device[position].max_image_size()):
        if device[position].image_size(modemap[mode], i)==size:
            s=i
            break
    if (s == -1):
        raise ValueError, "Size not supported for camera"

    if _my_camera.taking_photo():
        raise RuntimeError, "Photo request ongoing"

    if (mode == "JPEG_Exif" or mode == "JPEG_JFIF"):
        return _my_camera.take_photo(modemap[mode], s,
                                     zoom, flashmap[flash],
                                     exposuremap[exposure],
                                     whitebalancemap[white_balance],
                                     position)
    else:
        return graphics.Image.from_cfbsbitmap(_my_camera.take_photo(modemap[mode], s,
                                                                    zoom, flashmap[flash],
                                                                    exposuremap[exposure],
                                                                    whitebalancemap[white_balance],
                                                                    position))

def start_finder(call_back, backlight_on=1, size=_main_pane_size()):
    global _my_camera
    if _my_camera.finder_on():
        raise RuntimeError, "View finder is started already"
    global _my_call_back
    global _backlight_on
    if not callable(call_back):
        raise TypeError("'%s' is not callable"%call_back)
    _my_call_back=call_back
    _backlight_on=backlight_on
    _my_camera.start_finder(_finder_call_back, size)

def stop_finder():
    global _my_camera
    global _my_call_back
    _my_camera.stop_finder()
    _my_call_back=None

def video_formats(): # New!
    formats=[]
    supported = _my_camera.video_formats()
    for key in formatmap:
        if formatmap[key] & supported:
            formats.append(key)
    return formats

def frame_sizes(mode = 'YUV420p'): # New!
    sizes = []
    for i in range(_my_camera.GetNumVideoFrameSizesSupported()):
        s = _my_camera.frame_size(formatmap[mode], i)
        if s != (0, 0):
            sizes.append(s)
    return sizes

"""
Added by AltoRetrato:

On N82 we have:
    _my_camera.GetNumVideoFrameSizesSupported() = 2
    _my_camera.GetNumVideoFrameRatesSupported() = 30???
    camera.video_formats() = ['YUV420p', 'YUV422']
    camera.frame_sizes() =   [(640, 480), (320, 240), (176, 144)]
    camera.frame_rates() =   [
        {'size_index': 0, 'rate': 30.0, 'size': (640, 480)}, 
        {'size_index': 0, 'rate': 15.0, 'size': (640, 480)}, 
        {'size_index': 1, 'rate': 30.0, 'size': (320, 240)}, 
        {'size_index': 1, 'rate': 15.0, 'size': (320, 240)}, 
        {'size_index': 2, 'rate': 30.0, 'size': (176, 144)}, 
        {'size_index': 2, 'rate': 15.0, 'size': (176, 144)}
    ]
"""
def frame_rates(mode = "YUV420p", exposure = "auto"):
    rates = []
    for i in range(_my_camera.GetNumVideoFrameSizesSupported()):
        frameSize = _my_camera.frame_size(formatmap[mode], i)
        if (frameSize != (0, 0)):
            #for j in range(_my_camera.GetNumVideoFrameRatesSupported()):
            for j in range(int(_my_camera.GetNumVideoFrameRatesSupported())):
                frameRate = _my_camera.frame_rate(formatmap[mode], j, i, exposuremap[exposure])
                if (frameRate > 0.0):
                    #rates.append({"size": frameSize, "size_index": i, "rate": frameRate})
                    rates.append({"size": frameSize, "rate": frameRate})
    return rates

def image_modes():
    ret=[]
    modes=_my_camera.image_modes()
    for key in modemap:
        if (modes&modemap[key]):
            ret.append(key)
    return ret

def image_sizes(mode='RGB16'):
    sizes=[]
    for i in range(_my_camera.max_image_size()):
        s=_my_camera.image_size(modemap[mode], i)
        if s!=(0,0):
            sizes.append(s)
    return sizes

def max_zoom():
    return _my_camera.max_zoom()

def flash_modes():
    ret = []
    modes = _my_camera.flash_modes()
    for key in flashmap:
        if (modes&flashmap[key]):
            ret.append(key)
        if (flashmap[key]==0):
            ret.append(key)
    return ret

def exposure_modes():
    ret = []
    modes = _my_camera.exposure_modes()
    for key in exposuremap:
        if (modes&exposuremap[key]):
            ret.append(key)
        if (exposuremap[key]==0):
            ret.append(key)
    return ret

def white_balance_modes():
    ret = []
    modes = _my_camera.white_balance_modes()
    for key in whitebalancemap:
        if (modes&whitebalancemap[key]):
            ret.append(key)
        if (whitebalancemap[key]==0):
            ret.append(key)
    return ret

def cameras_available():
    return _my_camera.cameras_available()

def release():
    global _my_camera
    global number_of_devices
    global device
    _my_camera.release()
    for dev_index in range(number_of_devices):
        device[dev_index].release()

def _handle():
    return _my_camera.handle()

_my_video=_camera.Video()

def start_record(filename, cb, format = "YUV420p", frameSize = (176, 144), frameRate = 30.0, videoType = "", audioEnabled = True): #frameRate = 15.0):
    s = -1
    for i in range(_my_camera.GetNumVideoFrameSizesSupported()):
        if (_my_camera.frame_size(formatmap[format], i) == frameSize):
            s = i
            break
    #If we give invalid frameRate, it seems it chooses the closest valid one (e.g., when we give 5.0 it chooses 15.0)
    if (s == -1):
        raise ValueError, "Size not supported for capture"

    if (not _my_camera.finder_on()):
        raise RuntimeError, "View finder is not started"
    if (not callable(cb)):
        raise TypeError("'%s' is not callable" % cb)

    _my_video.start_record(_handle(), unicode(filename), cb, frameSize, formatmap[format], frameRate, videoType, audioEnabled)

    """
    #From: camera2_tests.py
    #iUO(ii)ifii
    #_my_video.start_record(_handle(), unicode(filename), cb, size, 25.0, 5000000, formatmap[format])
    #_my_video.start_record(_handle(), unicode(filename), cb, size, 25.0, 5000000, formatmap[format], 0) #videotype = 'H264AVCBPL31') #size=(640,480))
    #_my_video.start_record(_handle(), unicode(filename), cb, size, formatmap[format], 15.0, 5000000, 0) #videotype = 'H264AVCBPL31') #size=(640,480))
    #_my_video.start_record(_handle(), unicode(filename), cb, size, formatmap[format], 15.0, 5000000, 1) #videotype = 'H264AVCBPL31') #size=(640,480))
    #_my_video.start_record(_handle(), unicode(filename), cb, (320, 240), formatmap[format], 15.0, 5000000, 1) #videotype = 'H264AVCBPL31') #size=(640,480))
    #_my_video.start_record(_handle(), unicode(filename), cb, (320, 240), _camera.EFormatH264, 15.0, 5000000, 2) #videotype = 'H264AVCBPL31') #size=(640,480))
    """


def stop_record():
    _my_video.stop_record()
